/*
* Copyright (C) Microsoft. All rights reserved.  
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not 
* use this file except in compliance with the License.  You may obtain a copy 
* of the License at http://www.apache.org/licenses/LICENSE-2.0  
* 
* THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 
* WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 
* MERCHANTABLITY OR NON-INFRINGEMENT. 
*
* See the Apache License, Version 2.0 for specific language governing 
* permissions and limitations under the License.
*
*/
#include "pch.h"
#include <journal/common/setting_group_table.h>

using namespace std;
using namespace journal::services::settings::db;

namespace b = baja;

const b::uint32 setting_group_table::nameSizeInBytes = 255;

setting_group_table::setting_group_table()
{
    _settingsBlobCells = std::make_shared<std::vector<std::shared_ptr<s::icell>>>();
}

std::shared_ptr<journal::services::settings::db::setting_group_table> setting_group_table::create(std::shared_ptr<s::iengine> dbEngine)
{
    std::shared_ptr<journal::services::settings::db::setting_group_table> returnValue = make_shared<setting_group_table>();
    returnValue->_commonTableClient = std::dynamic_pointer_cast<icommon_table_client>(returnValue);
    returnValue->_commonTable = createCommonTable(returnValue->_commonTableClient, dbEngine, L"SettingsGroup");
    return returnValue;
}

std::shared_ptr<ct::icommon_table> setting_group_table::common()
{
    return _commonTable;
}

void setting_group_table::defineColumns(std::shared_ptr<s::itable> table)
{
    _nameColumn = s::createStringColumn(L"Name", s::column_type::type_string);
    _nameColumn->setMaxBufferSizeInBytes(nameSizeInBytes);
    table->addColumn(_nameColumn);

    _settingsBlobSizeColumn = s::createColumn<b::uint32>(L"SettingsBlobSize", s::column_type::type_int32);
    _settingsBlobSizeColumn->setIsBlobSize(true);
    table->addColumn(_settingsBlobSizeColumn);

    _settingsBlobColumn = s::createBlobColumn(L"SettingsBlob", s::column_type::type_binaryBlob);
    _settingsBlobColumn->setFixed(false);
    table->addColumn(_settingsBlobColumn);
}

void setting_group_table::defineIndexColumns(std::shared_ptr<s::iindex> primaryKeyIndex)
{
    _idIndexColumn = s::createIndexColumnDefinition(_nameColumn, true);
    primaryKeyIndex->addIndexColumn(_idIndexColumn);
}

void setting_group_table::defineRowCells(std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> row)
{
    _nameCell = _nameColumn->createCell(L"");
    _settingsBlobSizeCell = _settingsBlobSizeColumn->createCell(0);
    _settingsBlobCell = _settingsBlobColumn->createCellByMoving(s::binary_blob());

    row->push_back(_nameCell);
    row->push_back(_settingsBlobSizeCell);
    row->push_back(_settingsBlobCell);
}

void setting_group_table::write(
    std::wstring name,
    s::binary_blob& inBlob)
{
    _nameCell->setValue(name);
    _settingsBlobSizeCell->setValue(inBlob._size);
    _settingsBlobCell->setAppendOnUpdate(false);
    _settingsBlobCell->setValueByMoving(inBlob);

    if ( nullptr == findRow(name) )
    {
        _commonTable->insert(_commonTable->stagingRow());
    }
    else
    {
        _commonTable->update(_commonTable->stagingRow(),
            static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()));
    }
}

std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> setting_group_table::findRow(std::wstring name)
{
    // select columns...
    std::shared_ptr<s::iresult> result = s::createResult();
    result->addColumn(_nameColumn);
    result->addColumn(_settingsBlobSizeColumn);
    result->addColumn(_settingsBlobColumn);

    // where id = id
    _nameCell->setValue(name);
    _commonTable->read(_commonTable->stagingRow(),
                        static_cast<baja::uint32>(_commonTable->primaryIndex()->getIndexColumns()->size()),
                       result);

    // iterate through the result...
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = result->getRows();

    BAJA_ASSERT((rows->size() == 1 || rows->size() == 0), L"only 0 or 1 row is expected!");

    return rows->size() ? rows : nullptr;
}

bool setting_group_table::read(
    std::wstring name,
    s::binary_blob& outBlob)
{
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = findRow(name);

    if (rows->size() == 0)
    {
        return false;
    }

    std::vector<std::shared_ptr<s::iresult_row>>::iterator rowIt;
    for (rowIt = rows->begin(); rowIt < rows->end(); ++rowIt)
    {
        std::wstring groupFullName;

        // Bug 323 : TRACK: Gmileka: It'd be nice to have an easier way of getting directly to the cell we need...
        std::shared_ptr<s::iresult_row> row = *rowIt;
        std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> cells = row->getCells();
        std::vector<std::shared_ptr<s::icell>>::iterator cellIt;
        // There should be one cell per row only...
        for (cellIt = cells->begin(); cellIt < cells->end(); ++cellIt)
        {
            std::shared_ptr<s::icell> cell = *cellIt;
            if (cell->getColumnId() == _nameColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<std::wstring>> typedCell = std::dynamic_pointer_cast<s::icell_typed<std::wstring>>(cell);
                groupFullName = typedCell->getValue();
            }
            if (cell->getColumnId() == _settingsBlobColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<s::binary_blob>> typedCell = std::dynamic_pointer_cast<s::icell_typed<s::binary_blob>>(cell);
                typedCell->getValueByMoving(outBlob);
            }
        }
    }

    return true;
}

void setting_group_table::readAll(
    std::map<std::wstring, s::binary_blob>& outResult)
{
    // select columns...
    std::shared_ptr<s::iresult> result = s::createResult();
    result->addColumn(_nameColumn);
    result->addColumn(_settingsBlobSizeColumn);
    result->addColumn(_settingsBlobColumn);

    // where table.id = id
    _commonTable->readAll(result);

    // iterate through the result...
    std::shared_ptr<std::vector<std::shared_ptr<s::iresult_row>>> rows = result->getRows();

    std::vector<std::shared_ptr<s::iresult_row>>::iterator rowIt;
    for (rowIt = rows->begin(); rowIt < rows->end(); ++rowIt)
    {
        std::wstring groupFullName;

        // Bug 323 : TRACK: Gmileka: It'd be nice to have an easier way of getting directly to the cell we need...
        std::shared_ptr<s::iresult_row> row = *rowIt;
        std::shared_ptr<std::vector<std::shared_ptr<s::icell>>> cells = row->getCells();
        std::vector<std::shared_ptr<s::icell>>::iterator cellIt;
        // There should be one cell per row only...
        for (cellIt = cells->begin(); cellIt < cells->end(); ++cellIt)
        {
            std::shared_ptr<s::icell> cell = *cellIt;
            if (cell->getColumnId() == _nameColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<std::wstring>> typedCell = std::dynamic_pointer_cast<s::icell_typed<std::wstring>>(cell);
                groupFullName = typedCell->getValue();
            }
            if (cell->getColumnId() == _settingsBlobColumn->getId() )
            {
                std::shared_ptr<s::icell_typed<s::binary_blob>> typedCell = std::dynamic_pointer_cast<s::icell_typed<s::binary_blob>>(cell);

                outResult[groupFullName] = s::binary_blob(0);
                typedCell->getValueByMoving(outResult[groupFullName]);
            }
        }
    }
}
